package test.edu.demo.Filter.Xss.url;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
import org.springframework.web.util.UrlPathHelper;
import test.edu.demo.Filter.Xss.form.HTMLFilter;
import test.edu.demo.Filter.Xss.form.SQLFilter;

import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;

/**
 * @author xiao.hu
 * @date 2021-08-29
 * @apiNote
 *
 * BeanPostProcessor是Spring IOC容器给我们提供的一个扩展接口
 * 运行顺序:
 *
 * 1. Spring IOC容器实例化Bean
 * 2. 调用BeanPostProcessor的postProcessBeforeInitialization方法
 * 3. 调用bean实例的初始化方法
 * 4. 调用BeanPostProcessor的postProcessAfterInitialization方法
 *
 */
@Component
public class XssHandlerMappingPostProcessor implements BeanPostProcessor {
    private final static HTMLFilter htmlFilter = new HTMLFilter();

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException{
        if(bean instanceof AbstractHandlerMapping){
            /**
             * AbstractHandlerMapping 保存了所用配置的Interceptor，
             * 在获取 Handler 之后会自己根据从 request 中提取的 lookupPath 将相应的 Interceptors 装配上去。
             */
            AbstractHandlerMapping ahm = (AbstractHandlerMapping) bean;
            ahm.setUrlPathHelper(new XssUrlPathHelper());
        }
        return bean;
    }

    static class XssUrlPathHelper extends UrlPathHelper {
        @Override
        public Map<String, String> decodePathVariables(HttpServletRequest request, Map<String, String> vars){
            Map<String, String> result = super.decodePathVariables(request, vars);
            if(!CollectionUtils.isEmpty(result)){
                for(String key : result.keySet()){
                    result.put(key, cleanXSS(result.get(key)));
                }
            }
            return result;
        }

        @Override
        public MultiValueMap<String, String> decodeMatrixVariables(HttpServletRequest request, MultiValueMap<String, String> vars){
            MultiValueMap<String, String> mvm = super.decodeMatrixVariables(request, vars);
            if(!CollectionUtils.isEmpty(mvm)){
                for(String key : mvm.keySet()){
                    List<String> value = mvm.get(key);
                    for(int i = 0; i < value.size(); i++){
                        value.set(i, cleanXSS(value.get(i)));
                    }
                }
            }

            return mvm;
        }

        private String cleanXSS(String value){
            //return HtmlUtils.htmlEscape(value,"UTF-8");
            return SQLFilter.sqlInject(htmlFilter.filter(value));
        }

    }
}
